﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;

namespace ConsoleApplication1
{
    public class Parser
    {


        // ---------------------------------------------- Deklaration der Variablen ----------------------------------------------

        private String tmpMessage;
        private Backend backend;
        private List<String> messageFromServer;
        private Puffer puffer;
        private int serverNummer;
        //private bool blocked=false;

        // --------------------------------------------- Konstruktor Initialisierung ---------------------------------------------

        public Parser(Puffer puffer)
        {
            this.puffer = puffer;
            backend = new Backend(puffer.Connector);
        }


        // ------------------------------------------------ öffentliche Methoden -------------------------------------------------

        public void ReadFromPuffer()
        {
            List<String> naechstesElement;
            while (true)
            {

                if (!puffer.IsEmpty()) 
                {
                    lock (this.puffer)
                    {
                        naechstesElement = new List<string>(puffer.PopMessage());
                    }
                    SplitMessageFromServer(naechstesElement);
                }
            }
        }



        //Hilfsmethode zum splitten eines Strings und dieses Element wird gleich auf der messageFromServer gelöscht
        private String SplitString(String prüfen)
        {
            try
            {
                tmpMessage = messageFromServer.FirstOrDefault();
                    //Console.WriteLine(tmpMessage);
                string[] split = tmpMessage.Split(':');
                if (!split[0].Equals(prüfen)) { throw new Exception("Fehler: " + prüfen + " erwartet"); }

                //Falls mehrere : in einer Nachricht vorkommen
                if (split.Length > 2)
                {
                    int counter = 2;
                    do
                    {
                        split[1] += ":" + split[counter];
                        counter++;
                    } while (counter != split.Length);
                }

                tmpMessage = split[1];
                messageFromServer.RemoveAt(0);
            }
            catch (Exception e) { Console.WriteLine(e.Message); }

            return tmpMessage;

        }


        public void SplitMessageFromServer(List<String> msg)
        {

            this.messageFromServer = msg;
            try
            {
                if (this.messageFromServer != null)
                {
                    tmpMessage = SplitString("begin");
                    this.serverNummer = Convert.ToInt16(tmpMessage);        // Warum speichern wir die ServerNummer(Nachrichtennummer) im Objekt Parser?

                    tmpMessage = this.messageFromServer.FirstOrDefault();
                    if (tmpMessage.StartsWith("begin:"))
                    {
                        tmpMessage = SplitString("begin");
                        //Console.WriteLine("TEST" + tmpMessage);

                        switch (tmpMessage)
                        {
                            case "upd":
                                //Console.WriteLine("IM UPDATE");
                                Upd();
                                break;

                            case "del":
                                Del();
                                break;

                            case "map":
                                backend.SetMap(CreateMapContent());
                                break;

                            case "mes":
                                Message();
                                //backend.addMessage(message());
                                break;

                            case "result":               
                                backend.setResult(Result());
                                break;

                            case "challenge":
                                backend.setChallenge(Challenge());
                                break;

                            case "player":
                                Player();
                                //backend.addEntsPlayer(player());
                                break;

                            case "yourid":
                                backend.RefreshPlayer(YourId());
                                break;

                            case "time":
                                Time();
                                break;

                            case "online":
                                Online();
                                break;

                            case "ents":
                                Ents();
                                break;

                            case "players":
                                Players();
                                break;

                            default:
                                throw new Exception("Falsche Nachricht vom Server");
                        }
                    }
                    else if (tmpMessage.StartsWith("ans:"))
                    {
                        tmpMessage = SplitString("ans");
                        if (tmpMessage.StartsWith("unknown"))
                        {
                            Console.WriteLine(tmpMessage);
                            //backend.sendChat(tmpMessage);
                        }
                        else
                        {
                            switch (tmpMessage)
                            {
                                case "ok":
                                    //backend.sendAns(tmpMessage);
                                    break;

                                case "no":
                                    //backend.sendAns(tmpMessage);
                                    break;

                                case "invalid":
                                    //backend.sendAns(tmpMessage);
                                    break;

                                default:
                                    throw new Exception("Falsche Nachricht vom Server");
                            }
                        }
                    }

                    if (!(Convert.ToInt16(SplitString("end")) == serverNummer)) throw new Exception("Message nicht geschlossen");
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("PARSER: "+e.Message);
            }
        }

        private void Ents()
        {
            try
            {
                do
                {
                    tmpMessage = messageFromServer.FirstOrDefault();
                    if (!tmpMessage.StartsWith("begin:")) { throw new Exception("begin:dragon|player erwartet"); }
                    tmpMessage = SplitString("begin");
                    if (tmpMessage.Equals("dragon"))
                    {
                        backend.AddDragon(Dragon());
                    }
                    if (tmpMessage.Equals("player"))
                    {
                        backend.AddPlayer(Player());
                    }
                } while (!messageFromServer.FirstOrDefault().Equals("end:ents"));
                    if (!SplitString("end").Equals("ents")) throw new Exception("ents nicht geschlossen");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }


        private void Del()
        {
            try
            {
                tmpMessage = messageFromServer.FirstOrDefault();
                if (!tmpMessage.StartsWith("begin:")) { throw new Exception("begin:dragon|player erwartet"); }
                tmpMessage = SplitString("begin");
                if (tmpMessage.Equals("dragon"))
                {                   
                    backend.RemoveDragon(Dragon());
                }
                if (tmpMessage.Equals("player"))
                {
                   
                    backend.RemovePlayer(Player());
                }
                if (!SplitString("end").Equals("del")) throw new Exception("del nicht geschlossen");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        private void Players()
        {
            try
            {
                do
                {
                    tmpMessage = messageFromServer.FirstOrDefault();
                    if (!tmpMessage.StartsWith("begin:"))
                    {
                        throw new Exception("begin:player erwartet");
                    }
                    tmpMessage = SplitString("begin");
                    Player();
                    //backend.addPlayer(player());
                } while (!messageFromServer.FirstOrDefault().Equals("end:players"));

                SplitString("end");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }


        //Update von Player, Drache oder Mapcell
        private void Upd()
        {
            try
            {
                tmpMessage = messageFromServer.FirstOrDefault();
                if (!tmpMessage.StartsWith("begin:")) { throw new Exception("begin: dragon|cell|player erwartet"); }
                tmpMessage = SplitString("begin");
                if (tmpMessage.Equals("dragon"))
                {
                    backend.AddDragon(Dragon());
                }
                if (tmpMessage.Equals("cell"))
                {
                    MapCell();
                }
                if (tmpMessage.Equals("player"))
                {
                    backend.AddPlayer(Player());
                }
                if (!SplitString("end").Equals("upd")) throw new Exception("upd nicht geschlossen");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }


        private Player Player()
        {
            int points;
            int id;
            Boolean busy;
            String desc;
            int x;
            int y;
            Player player = null;

            try
            {
                id = Convert.ToInt32(SplitString("id"));

                if (!SplitString("type").Equals("Player")) throw new Exception("player typ falsch!");


                busy = Convert.ToBoolean(SplitString("busy"));
                desc = SplitString("desc");
                x = Convert.ToInt32(SplitString("x"));
                y = Convert.ToInt32(SplitString("y"));
                points = Convert.ToInt16(SplitString("points"));
                player = new Player(id, x, y, desc, busy, points);


                if (!SplitString("end").Equals("player")) throw new Exception("player nicht geschlossen");

              //  Console.WriteLine("points:" + points + " id:" + id + " busy:" + busy + " desc:" + desc + " x:" + x + " y:" + y);
                //(Console.WriteLine("Player Erstellt. ID: " + id);

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            return player;
        }


        private Dragon Dragon()
        {
            //Console.WriteLine("IM DRAGON");
            int id;
            Boolean busy;
            String desc;
            int x;
            int y;
            Dragon dragon = null;
            try
            {
                id = Convert.ToInt32(SplitString("id"));
                if (!SplitString("type").Equals("Dragon")) throw new Exception("dragon type falsch!");

                busy = Convert.ToBoolean(SplitString("busy"));
                desc = SplitString("desc");
                x = Convert.ToInt32(SplitString("x"));
                y = Convert.ToInt32(SplitString("y"));
                dragon = new Dragon(id, busy, desc, x, y);


                if (!SplitString("end").Equals("dragon")) throw new Exception("dragon nicht geschlossen");

                //Console.WriteLine("id:" + id + " busy:" + busy + " desc:" + desc + " x:" + x + " y:" + y);
                Console.WriteLine("Dragon Erstellt. ID: " + id);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return dragon;
        }


        /*
         * Methode challenge zum Parsen von Challenge-Anfragen. Funktioniert mit dummydaten.
         * (Benjamin)
         */

        private Challenge Challenge()
        {
            int id;
            String type;
            bool accepted;
            Challenge challenge = null;

            try
            {
                if (!this.messageFromServer[0].StartsWith("id:")) { throw new Exception("Fehler in Challenge: Id erwartet!"); }

                id = Convert.ToInt16(SplitString("id"));
                type = SplitString("type");
                accepted = Convert.ToBoolean(SplitString("accepted"));

                if (!(SplitString("end").Equals("challenge"))) { throw new Exception("Fehler: Challenge nicht geschlossen!"); }

                challenge = new Challenge(id, type, accepted);
                //backend.setChallenge(challenge);

                //Console.WriteLine("Id:" + id + " Typ:" + type + " Accepted:" + accepted); // Testausgabe auf Console
                Console.WriteLine("Challange Erstellt. ID: " + id + " Typ:" + type + " Accepted:" + accepted);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return challenge;
        }


        /* Parsen von Message, die vom Server kommt. Was machen wir mit Message? Hier an das Backend weitergeben um in 
         * der Gui anzuzeigen? Mehr muss man ja nicht machen....
         * (benjamin)
         */

        private String Message()
        {
            int srcid;
            string src;
            string text;
            string s = null;

            try
            {
                if (!this.messageFromServer[0].StartsWith("srcid:")) { throw new Exception("Fehler: SrcId erwartet!"); }

                srcid = Convert.ToInt16(SplitString("srcid"));
                src = SplitString("src");
                text = SplitString("txt");

                Console.WriteLine("MessageIncome: " + " SrcId: " + srcid + " Src: " + src + " Text: " + text);
                s = "MessageIncome: " + " SrcId: " + srcid + " Src: " + src + " Text: " + text;
                backend.ChatMessageToGui(src , text);
     
                if (!(SplitString("end").Equals("mes"))) { throw new Exception("Fehler: message nicht geschlossen!"); }

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return s;
        }


        private Result Result()
        {
            int round;
            bool running;
            int delay;
            Opponent opponent1 = null;
            Opponent opponent2 = null;
            Result result = null;

            try
            {
                round = Convert.ToInt32(SplitString("round"));
                running = Convert.ToBoolean(SplitString("running"));
                delay = Convert.ToInt32(SplitString("delay"));

                SplitString("begin");
                SplitString("begin");
                opponent1 = Opponent();
                SplitString("begin");
                opponent2 = Opponent();

                result = new Result(round, running, delay, opponent1, opponent2);


                if (!SplitString("end").Equals("opponents")) throw new Exception("opponents ist nicht geschlossen");
                if (!SplitString("end").Equals("result")) throw new Exception("result nicht geschlossen");

                Console.WriteLine("round:" + round + " running:" + running + " delay:" + delay + " opponent1" + opponent1 + " opponent2" + opponent2);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

            return result;
        }


        private Opponent Opponent()
        {

            int id;
            String decision = null;
            int points;
            int total;
            Opponent opponent = null;

            try
            {
                id = Convert.ToInt32(SplitString("id"));
                decision = SplitString("decision");
                points = Convert.ToInt32(SplitString("points"));
                total = Convert.ToInt32(SplitString("total"));

                opponent = new Opponent(id, decision, points, total);

                if (!SplitString("end").Equals("opponent")) throw new Exception("opponent ist nicht geschlossen");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return opponent;  
        }


        //Erstellt die Map
        private MapContent CreateMapContent()
        {

            MapContent map = null;
            int width = 0;
            int height = 0;
            MapCell[][] cellArray = null;
            MapCell cell;

            try
            {
                width = Convert.ToInt32(SplitString("width"));
                height = Convert.ToInt32(SplitString("height"));
                cellArray = new MapCell[width][];

                for(int i=0;i<height;i++){
                    cellArray[i] = new MapCell[height];
                }

                Console.WriteLine("MAP: width: " + width + " ,height:" + height);

                if (!SplitString("begin").Equals("cells")) throw new Exception("Keine Zellen");
                do
                {

                    if (!SplitString("begin").Equals("cell")) throw new Exception("Keine Zelle");
                    cell = MapCell();
                    cellArray[cell.X][cell.Y] = cell;

                } while (!messageFromServer.FirstOrDefault().Equals("end:cells"));

                messageFromServer.RemoveAt(0);
                if (!SplitString("end").Equals("map")) throw new Exception("Map-Ende fehlt");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            map = new MapContent(width, height, cellArray);
            return map;
        }


        //Erstellt die Map-Zellen
        private MapCell MapCell()
        {
            MapCell cell = null;
            int row;
            int column;

            //ZellenEigenschaften
            bool isWalkable = false;
            bool isHuntable = false;
            bool isForest = false;
            bool isWater = false;

            try
            {
                row = Convert.ToInt32(SplitString("row"));
                column = Convert.ToInt32(SplitString("col"));

                //ZellenEigenschaften
                if (!SplitString("begin").Equals("props")) throw new Exception("Keine Properties");
                do
                {
                    if (messageFromServer.FirstOrDefault().Equals("WALL"))
                    {
                        isWalkable = false;
                        messageFromServer.RemoveAt(0);
                    }
                    if (messageFromServer.FirstOrDefault().Equals("WALKABLE"))
                    {
                        isWalkable = true;
                        messageFromServer.RemoveAt(0);
                    }
                    if (messageFromServer.FirstOrDefault().Equals("FOREST"))
                    {
                        isForest = true;
                        messageFromServer.RemoveAt(0);
                    }
                    if (messageFromServer.FirstOrDefault().Equals("WATER"))
                    {
                        isWater = true;
                        messageFromServer.RemoveAt(0);
                    }
                    if (messageFromServer.FirstOrDefault().Equals("HUNTABLE"))
                    {
                        isHuntable = true;
                        messageFromServer.RemoveAt(0);
                    }

                } while (!messageFromServer.FirstOrDefault().Equals("end:props"));
                messageFromServer.RemoveAt(0);

                //Erstellt die Zelle
                cell = new MapCell(row, column, isWalkable, isHuntable, isForest, isWater);
                //Console.WriteLine("ZELLE row: " + row + " ,col: " + column + " ,Walkable: " + isWalkable + " , Huntable: " + isHuntable + " ,Forest: " + isForest + " , Water: " + isWater);
                //Console.WriteLine("Zelle Erstellt. ROW: " + row + " COL: " + column);

                //Setzt die Eigenschaften wieder zur Anfangseinstellung für die nächste Zelle
                isWalkable = false;
                isHuntable = false;
                isForest = false;
                isWater = false;

                if (!SplitString("end").Equals("cell")) throw new Exception("Keine Zelle");
                return cell;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return cell;
        }


        //Gibt die ID zurück
        private int YourId()
        {
            int id = 0;
            try
            {
                id = Convert.ToInt32(messageFromServer.FirstOrDefault());
                messageFromServer.RemoveAt(0);
                if (!SplitString("end").Equals("yourid")) throw new Exception("Keine eigene ID");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            
            return id;
        }


        //Gibt die Zeit zurück
        private long Time()
        {
            long time = 0;
            try
            {
                time = Convert.ToInt64(messageFromServer.FirstOrDefault());
                Console.WriteLine("Zeit: " + time);
                messageFromServer.RemoveAt(0);
                if (!SplitString("end").Equals("time")) throw new Exception("Keine Zeit");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return time;
        }


        //Gibt den Online-Status zurück
        private int Online()
        {
            int online = 0;
            try
            {
                online = Convert.ToInt32(messageFromServer.FirstOrDefault());
                Console.WriteLine("Online-Status: " + online);
                messageFromServer.RemoveAt(0);
                if (!SplitString("end").Equals("online")) throw new Exception("Keine eigene ID");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
            return online;
        }
    }
}
